/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package poproject.OptimizationTask;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

/**
 * Reprezentuje dane dotyczące mapy, czyli:
 * 1. Parametry typu liczba obszarów oraz liczba poziomów
 * 2. Strefy zabronione
 * 3. ...
 * 
 * Definicje niektórych terminów:
 * - obszar (area) - pewien fragment mapy, któremu przypisano koszt budowy trasy
 * przebiegającej przez ten fragment
 * 
 * - poziom (level) - położenie "w pionie" na zdyskretyzowanej mapie.
 * 
 * - pozycja (position) - położenie w "poziomie". Pozycje znajdują się jakby między obszarami
 * i w nich stawia się wierzchołki trasy. Przy N obszarach pozycji jest N+1
 * 
 * - przeszkoda (obstacle) - zbiór poziomów zabronionych wraz z pozycją określającą
 * położenie przeszkody.
 * 
 * 
 * @author Eruanno
 */
public class MapModel {
    
    private int numOfAreas;     ///< Liczba obszarów na jakie podzielono mapę
    private int numOfLevels;    ///< Liczba poziomów (czyli możliwości ruchu w pionie).
    
    /**
     * Kontener na przeszkody. Jeden wpis odpowiada jednej (pozycji).
     */
    private HashMap<Integer, MapObstacle> obstacles = new HashMap<>();
    
    /**
     * Aktualnie wytyczona trasa.
     */
    private MapTrack currentTrack;
    
    /**
     * Koszty przypadające obszarom
     */
    private int[] areaCosts; 
    
    /**
     * Poziom początkowy (punkt startowy trasy)
     */
    private int startLevel;
    
    /**
     * Poziom końcowy (punkt końcowy trasy)
     */
    private int finishLevel;
    
    /**
     * Tworzy nową instancję mapy z zadaną ilością obszarów i poziomów.
     * @param areas
     * @param levels 
     */
    public MapModel(int areas, int levels) {
        this.numOfAreas = areas;
        this.numOfLevels = levels;
 
        currentTrack = new MapTrack(this);
        
        startLevel = finishLevel = 0;
        
        areaCosts = new int[areas];
    }
    
    /**
     * Przypisuje obszarowi koszt. Koszt może być ujemny (np. dofinansowanie
     * z Unii ;) )
     * @param areaNum numer obszaru. Pierwszy obszar to ten na prawo od 0 pozycji
     * i na lewo 1. pozycji.
     * @param cost Koszt obszaru
     * @return całkowity koszt budowy trasy
     */
    public void setAreaCost(int areaNum, int cost)
    {   
        assert(areaNum >= 0 && areaNum < numOfAreas) : "Numer obszaru poza dozwolonym przedziałem.";
        
        areaCosts[areaNum] = cost;
    }
    
    /**
     * Zwraca tabelę kosztów budowy na zadanym obszarze.
     */
    public ArrayList<Integer> areaCosts() {
        ArrayList<Integer> list = new ArrayList<>(numOfAreas);
        
        for(int i = 0; i < numOfAreas; i++) {
            list.add(areaCosts[i]);
        }
        
        return list;
    }
    
    /**
     * Zwraca koszt budowy na zadanym obszarze.
     * @param areaNum Koszt budowy.
     */
    public int areaCost(int areaNum) {
        assert(areaNum >= 0 && areaNum < numOfAreas) : "Numer obszaru poza dozwolonym przedziałem.";
        
        return areaCosts[areaNum];
    }
    
    /**
     * Zwraca liczbę poziomów mapy.
     * @return Liczba poziomów.
     */
    public int numOfLevels() {
        return numOfLevels;
    }
    
    /**
     * Zwraca liczbę obszarów mapy.
     * @return Liczba obszarów.
     */
    public int numOfAreas() {
        return numOfAreas;
    }
    
    /**
     * Zwraca punkt początkowy trasy (poziom tego punktu)
     * @return Poziom punktu startowego trasy
     */
    public int startLevel() {
        return startLevel;
    }
    
    /**
     * Zwraca punkt końcowy trasy (poziom tego punktu)
     * @return Poziom punktu końcowego trasy
     */
    public int finishLevel() { 
        return finishLevel;
    }
    
    /**
     * Ustawia punkt początkowy trasy (poziom tego punktu)
     * @param level Zadany poziom początkowy
     */
    public void setStartLevel(int level) {
        startLevel = level;
    }
    
    /**
     * Ustawia punkt końcowy trasy (poziom tego punktu)
     * @param level Zadany punkt końcowy
     */
    public void setFinishLevel(int level) { 
        finishLevel = level;
    }
    
    /**
     * Dodaje do mapy przeszkodę.
     * @param obstacle Obiekt reprezentujący przeszkodę.
     */
    public void addObstacle(MapObstacle obstacle)
    {
        assert(obstacle.position() >= 0 && obstacle.position() <= numOfAreas) : "Próba dodania przeszkody dla nieistniejącej pozycji.";
           
        obstacles.put(obstacle.position(), obstacle);
    }
    
     /**
     * Dodaje do mapy pojedyńczą przeszkodę.
     * @param obstacle Obiekt reprezentujący przeszkodę (nr poziomu + numer pozycji).
     */
    public void addObstacle(int position, int level)
    {
        if(obstacles.get(position) == null)
            obstacles.put(position, new MapObstacle(this, position));
        
       obstacles.get(position).addRestricted(level);
    }
    
    /**
     * Przełącza przeszkodę.
     * Jeśli wcześniej była przeszkoda to ją usuwa, jeśli nie było stawia nową.
     */
    public void toggleObstacle(int position, int level)
    {
        if(obstacles.get(position) == null)
            obstacles.put(position, new MapObstacle(this, position));
        
       obstacles.get(position).toggleRestricted(level);
    }
    
    /**
     * Zwraca przeszkodę dla zadanej pozycji.
     * 
     * @param position Pozycja
     * @return Obiekt reprezentujący przeszkodę lub null jeśli niezdefiniowano
     * przeszkody dla zadanej pozycji.
     */
    public MapObstacle obstacle(int position) {
        assert (position >= 0 && position <= numOfAreas) : "Próba pobrania przeszkody z nieistniejącej pozycji.";

        if (obstacles.containsKey(position)) {
            return obstacles.get(position);
        } else {
            return null;
        }
    }

    /**
     * Zwraca aktualnie przypisaną mapie ścieżkę.
     * @return 
     */
    public MapTrack currentTrack() {
        return currentTrack;
    }
    
    /**
     * Ustawia nową ścieżkę.
     * @param track 
     */
    public void setCurrentTrack(MapTrack track) {
        /// TODO: error checking
        this.currentTrack = track;
    }
}
